@@ -1,4 +1,5 @@  | 
            ||
| 1 | 1 | 
                *.db  | 
            
| 2 | 
                +*.rdb  | 
            |
| 2 | 3 | 
                *.swp  | 
            
| 3 | 4 | 
                *.idea  | 
            
| 4 | 5 | 
                *.sqlite3  | 
            
                @@ -0,0 +1,33 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.conf import settings  | 
            |
| 4 | 
                +from django.contrib import admin  | 
            |
| 5 | 
                +from django.contrib.auth.hashers import make_password  | 
            |
| 6 | 
                +from pysnippets.strsnippets import strip  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +from account.models import AdministratorInfo  | 
            |
| 9 | 
                +from equipment.models import IsolationPointInfo  | 
            |
| 10 | 
                +  | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                +class AdministratorInfoAdmin(admin.ModelAdmin):  | 
            |
| 13 | 
                +    list_display = ('admin_id', 'phone', 'password', 'encryption', 'name', 'point_id', 'point_name', 'user_status', 'status', 'created_at', 'updated_at')
               | 
            |
| 14 | 
                +    list_filter = ('user_status', 'status', 'point_name')
               | 
            |
| 15 | 
                +    readonly_fields = ('encryption', 'point_name')
               | 
            |
| 16 | 
                +  | 
            |
| 17 | 
                + def save_model(self, request, obj, form, change):  | 
            |
| 18 | 
                + obj.phone = strip(obj.phone)  | 
            |
| 19 | 
                + obj.password = strip(obj.password)  | 
            |
| 20 | 
                + if obj.password:  | 
            |
| 21 | 
                + obj.encryption = make_password(obj.password, settings.MAKE_PASSWORD_SALT, settings.MAKE_PASSWORD_HASHER)  | 
            |
| 22 | 
                + obj.password = ''  | 
            |
| 23 | 
                +  | 
            |
| 24 | 
                + obj.point_id = strip(obj.point_id)  | 
            |
| 25 | 
                + try:  | 
            |
| 26 | 
                + obj.point_name = IsolationPointInfo.objects.get(point_id=obj.point_id).point_name  | 
            |
| 27 | 
                + except IsolationPointInfo.DoesNotExist:  | 
            |
| 28 | 
                + obj.point_name = ''  | 
            |
| 29 | 
                +  | 
            |
| 30 | 
                + obj.save()  | 
            |
| 31 | 
                +  | 
            |
| 32 | 
                +  | 
            |
| 33 | 
                +admin.site.register(AdministratorInfo, AdministratorInfoAdmin)  | 
            
                @@ -0,0 +1,5 @@  | 
            ||
| 1 | 
                +from django.apps import AppConfig  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +class AccountConfig(AppConfig):  | 
            |
| 5 | 
                + name = 'account'  | 
            
                @@ -0,0 +1,36 @@  | 
            ||
| 1 | 
                +# Generated by Django 3.2.4 on 2021-07-09 01:41  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.db import migrations, models  | 
            |
| 4 | 
                +import shortuuidfield.fields  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +class Migration(migrations.Migration):  | 
            |
| 8 | 
                +  | 
            |
| 9 | 
                + initial = True  | 
            |
| 10 | 
                +  | 
            |
| 11 | 
                + dependencies = [  | 
            |
| 12 | 
                + ]  | 
            |
| 13 | 
                +  | 
            |
| 14 | 
                + operations = [  | 
            |
| 15 | 
                + migrations.CreateModel(  | 
            |
| 16 | 
                + name='AdministratorInfo',  | 
            |
| 17 | 
                + fields=[  | 
            |
| 18 | 
                +                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
               | 
            |
| 19 | 
                +                ('status', models.BooleanField(default=True, help_text='Status', verbose_name='status')),
               | 
            |
| 20 | 
                +                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
               | 
            |
| 21 | 
                +                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
               | 
            |
| 22 | 
                +                ('admin_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='管理员唯一标识', max_length=22, null=True, unique=True)),
               | 
            |
| 23 | 
                +                ('phone', models.CharField(blank=True, db_index=True, help_text='管理员电话', max_length=11, null=True, verbose_name='phone')),
               | 
            |
| 24 | 
                +                ('password', models.CharField(blank=True, help_text='管理员密码', max_length=255, null=True, verbose_name='password')),
               | 
            |
| 25 | 
                +                ('encryption', models.CharField(blank=True, help_text='管理员密码', max_length=255, null=True, verbose_name='encryption')),
               | 
            |
| 26 | 
                +                ('name', models.CharField(blank=True, help_text='管理员姓名', max_length=255, null=True, verbose_name='name')),
               | 
            |
| 27 | 
                +                ('point_id', models.CharField(blank=True, db_index=True, help_text='隔离点唯一标识', max_length=32, null=True, verbose_name='point_id')),
               | 
            |
| 28 | 
                +                ('point_name', models.CharField(blank=True, help_text='隔离点名称', max_length=255, null=True, verbose_name='point_name')),
               | 
            |
| 29 | 
                +                ('user_status', models.IntegerField(choices=[(1, '已激活'), (2, '已禁用'), (3, '已删除')], db_index=True, default=1, help_text='管理员状态', verbose_name='user_status')),
               | 
            |
| 30 | 
                + ],  | 
            |
| 31 | 
                +            options={
               | 
            |
| 32 | 
                + 'verbose_name': '管理员信息',  | 
            |
| 33 | 
                + 'verbose_name_plural': '管理员信息',  | 
            |
| 34 | 
                + },  | 
            |
| 35 | 
                + ),  | 
            |
| 36 | 
                + ]  | 
            
                @@ -0,0 +1,49 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.db import models  | 
            |
| 4 | 
                +from django.utils.translation import ugettext_lazy as _  | 
            |
| 5 | 
                +from django_models_ext import BaseModelMixin  | 
            |
| 6 | 
                +from shortuuidfield import ShortUUIDField  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +from equipment.models import IsolationPointInfo  | 
            |
| 9 | 
                +  | 
            |
| 10 | 
                +  | 
            |
| 11 | 
                +class AdministratorInfo(BaseModelMixin):  | 
            |
| 12 | 
                + ACTIVATED = 1  | 
            |
| 13 | 
                + DISABLED = 2  | 
            |
| 14 | 
                + DELETED = 3  | 
            |
| 15 | 
                +  | 
            |
| 16 | 
                + USER_STATUS_TUPLE = (  | 
            |
| 17 | 
                + (ACTIVATED, '已激活'),  | 
            |
| 18 | 
                + (DISABLED, '已禁用'),  | 
            |
| 19 | 
                + (DELETED, '已删除'),  | 
            |
| 20 | 
                + )  | 
            |
| 21 | 
                +  | 
            |
| 22 | 
                +    admin_id = ShortUUIDField(_('admin_id'), max_length=32, blank=True, null=True, help_text='管理员唯一标识', db_index=True, unique=True)
               | 
            |
| 23 | 
                +  | 
            |
| 24 | 
                +    phone = models.CharField(_('phone'), max_length=11, blank=True, null=True, help_text='管理员电话', db_index=True)
               | 
            |
| 25 | 
                +    password = models.CharField(_('password'), max_length=255, blank=True, null=True, help_text='管理员密码')
               | 
            |
| 26 | 
                +    encryption = models.CharField(_('encryption'), max_length=255, blank=True, null=True, help_text='管理员密码')
               | 
            |
| 27 | 
                +  | 
            |
| 28 | 
                +    name = models.CharField(_('name'), max_length=255, blank=True, null=True, help_text='管理员姓名')
               | 
            |
| 29 | 
                +  | 
            |
| 30 | 
                +    point_id = models.CharField(_('point_id'), max_length=32, blank=True, null=True, help_text='隔离点唯一标识', db_index=True)
               | 
            |
| 31 | 
                +    point_name = models.CharField(_('point_name'), max_length=255, blank=True, null=True, help_text='隔离点名称')
               | 
            |
| 32 | 
                +  | 
            |
| 33 | 
                +    user_status = models.IntegerField(_('user_status'), choices=USER_STATUS_TUPLE, default=ACTIVATED, help_text='管理员状态', db_index=True)
               | 
            |
| 34 | 
                +  | 
            |
| 35 | 
                + class Meta:  | 
            |
| 36 | 
                +        verbose_name = _('管理员信息')
               | 
            |
| 37 | 
                +        verbose_name_plural = _('管理员信息')
               | 
            |
| 38 | 
                +  | 
            |
| 39 | 
                + def __unicode__(self):  | 
            |
| 40 | 
                +        return '{}-{}'.format(self.name, self.phone)
               | 
            |
| 41 | 
                +  | 
            |
| 42 | 
                + @property  | 
            |
| 43 | 
                + def data(self):  | 
            |
| 44 | 
                +        return {
               | 
            |
| 45 | 
                + 'admin_id': self.admin_id,  | 
            |
| 46 | 
                + 'name': self.name,  | 
            |
| 47 | 
                + 'point_id': self.point_id,  | 
            |
| 48 | 
                + 'point_name': self.point_name,  | 
            |
| 49 | 
                + }  | 
            
                @@ -0,0 +1,4 @@  | 
            ||
| 1 | 
                +from django.test import TestCase  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +# Create your tests here.  | 
            
                @@ -0,0 +1,4 @@  | 
            ||
| 1 | 
                +from django.shortcuts import render  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +# Create your views here.  | 
            
                @@ -0,0 +1,41 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from __future__ import division  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +from django.conf import settings  | 
            |
| 6 | 
                +from django.contrib.auth.hashers import check_password  | 
            |
| 7 | 
                +from django_logit import logit  | 
            |
| 8 | 
                +from django_response import response  | 
            |
| 9 | 
                +  | 
            |
| 10 | 
                +from account.models import AdministratorInfo  | 
            |
| 11 | 
                +from equipment.models import IsolationPointInfo  | 
            |
| 12 | 
                +from utils.error.errno_utils import AdministratorStatusCode, IsolationPointStatusCode  | 
            |
| 13 | 
                +  | 
            |
| 14 | 
                +  | 
            |
| 15 | 
                +WECHAT = settings.WECHAT  | 
            |
| 16 | 
                +  | 
            |
| 17 | 
                +  | 
            |
| 18 | 
                +@logit  | 
            |
| 19 | 
                +def admin_login(request):  | 
            |
| 20 | 
                +    phone = request.POST.get('phone', '')
               | 
            |
| 21 | 
                +    password = request.POST.get('password', '')
               | 
            |
| 22 | 
                +  | 
            |
| 23 | 
                + try:  | 
            |
| 24 | 
                + administrator = AdministratorInfo.objects.get(phone=phone, status=True)  | 
            |
| 25 | 
                + except AdministratorInfo.DoesNotExist:  | 
            |
| 26 | 
                + return response(AdministratorStatusCode.ADMINISTRATOR_NOT_FOUND)  | 
            |
| 27 | 
                +  | 
            |
| 28 | 
                + if administrator.user_status == AdministratorInfo.DISABLED:  | 
            |
| 29 | 
                + return response(AdministratorStatusCode.ADMINISTRATOR_NOT_ACTIVATED)  | 
            |
| 30 | 
                + elif administrator.user_status == AdministratorInfo.DELETED:  | 
            |
| 31 | 
                + return response(AdministratorStatusCode.ADMINISTRATOR_HAS_DELETED)  | 
            |
| 32 | 
                +  | 
            |
| 33 | 
                + if not check_password(password, administrator.encryption):  | 
            |
| 34 | 
                + return response(AdministratorStatusCode.ADMINISTRATOR_PASSWORD_ERROR)  | 
            |
| 35 | 
                +  | 
            |
| 36 | 
                + try:  | 
            |
| 37 | 
                + point = IsolationPointInfo.objects.get(point_id=administrator.point_id, status=True)  | 
            |
| 38 | 
                + except IsolationPointInfo.DoesNotExist:  | 
            |
| 39 | 
                + return response(IsolationPointStatusCode.ISOLATIONPOINT_NOT_FOUND)  | 
            |
| 40 | 
                +  | 
            |
| 41 | 
                +    return response(200, 'Admin Login Success', '管理员登录成功', data={**administrator.data, **point.data})
               | 
            
                @@ -0,0 +1,121 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from __future__ import division  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +from django.db.models import Q  | 
            |
| 6 | 
                +from django_logit import logit  | 
            |
| 7 | 
                +from django_response import response  | 
            |
| 8 | 
                +from TimeConvert import TimeConvert as tc  | 
            |
| 9 | 
                +  | 
            |
| 10 | 
                +from equipment.models import (IsolationPointInfo, ThermometerEquipmentInfo, ThermometerMeasureInfo,  | 
            |
| 11 | 
                + ThermometerMeasureLogInfo)  | 
            |
| 12 | 
                +from utils.error.errno_utils import IsolationPointStatusCode, ThermometerEquipmentStatusCode  | 
            |
| 13 | 
                +  | 
            |
| 14 | 
                +  | 
            |
| 15 | 
                +@logit  | 
            |
| 16 | 
                +def eqpt_bind(request):  | 
            |
| 17 | 
                +    point_id = request.POST.get('point_id', '')
               | 
            |
| 18 | 
                +    macid = request.POST.get('macid', '')
               | 
            |
| 19 | 
                +    sn = request.POST.get('sn', '')
               | 
            |
| 20 | 
                +  | 
            |
| 21 | 
                +    ThermometerEquipmentInfo.objects.update_or_create(macid=macid, defaults={
               | 
            |
| 22 | 
                + 'point_id': point_id,  | 
            |
| 23 | 
                + 'sn': sn,  | 
            |
| 24 | 
                + })  | 
            |
| 25 | 
                +  | 
            |
| 26 | 
                + return response()  | 
            |
| 27 | 
                +  | 
            |
| 28 | 
                +  | 
            |
| 29 | 
                +@logit  | 
            |
| 30 | 
                +def eqpt_onoff(request):  | 
            |
| 31 | 
                +    macid = request.POST.get('macid', '')
               | 
            |
| 32 | 
                +    active = request.POST.get('active', 0)
               | 
            |
| 33 | 
                +  | 
            |
| 34 | 
                +    ThermometerEquipmentInfo.objects.update_or_create(macid=macid, defaults={
               | 
            |
| 35 | 
                + 'active_status': active,  | 
            |
| 36 | 
                + 'active_at': tc.utc_datetime(),  | 
            |
| 37 | 
                + })  | 
            |
| 38 | 
                +  | 
            |
| 39 | 
                + return response()  | 
            |
| 40 | 
                +  | 
            |
| 41 | 
                +  | 
            |
| 42 | 
                +@logit  | 
            |
| 43 | 
                +def eqpt_list(request):  | 
            |
| 44 | 
                +    point_id = request.POST.get('point_id', '')
               | 
            |
| 45 | 
                +    macid = request.POST.get('macid', '')
               | 
            |
| 46 | 
                +  | 
            |
| 47 | 
                + eqpts = ThermometerEquipmentInfo.objects.filter(point_id=point_id, status=True)  | 
            |
| 48 | 
                + if macid:  | 
            |
| 49 | 
                + eqpts = eqpts.filter(macid=macid)  | 
            |
| 50 | 
                + eqpts = [eqpt.data for eqpt in eqpts]  | 
            |
| 51 | 
                +  | 
            |
| 52 | 
                + total_num = len(eqpts)  | 
            |
| 53 | 
                +    active_num = len([1 for eqpt in eqpts if eqpt.get('active_status') == ThermometerEquipmentInfo.ONLINE])
               | 
            |
| 54 | 
                +  | 
            |
| 55 | 
                +    return response(data={
               | 
            |
| 56 | 
                + 'eqpts': eqpts,  | 
            |
| 57 | 
                + 'total_num': total_num,  | 
            |
| 58 | 
                + 'active_num': active_num,  | 
            |
| 59 | 
                + 'unactive_num': total_num - active_num,  | 
            |
| 60 | 
                + })  | 
            |
| 61 | 
                +  | 
            |
| 62 | 
                +  | 
            |
| 63 | 
                +@logit  | 
            |
| 64 | 
                +def eqpt_result(request):  | 
            |
| 65 | 
                +    point_id = request.POST.get('point_id', '')
               | 
            |
| 66 | 
                +    kw = request.POST.get('kw', '')
               | 
            |
| 67 | 
                +  | 
            |
| 68 | 
                + try:  | 
            |
| 69 | 
                + point = IsolationPointInfo.objects.get(point_id=point_id, status=True)  | 
            |
| 70 | 
                + except IsolationPointInfo.DoesNotExist:  | 
            |
| 71 | 
                + return response(IsolationPointStatusCode.ISOLATIONPOINT_NOT_FOUND)  | 
            |
| 72 | 
                +  | 
            |
| 73 | 
                +    logs = ThermometerMeasureInfo.objects.filter(point_id=point_id, point_measure_ymd=tc.local_string(format='%Y-%m-%d'), point_measure_window=point.point_measure_window, status=True).order_by('-pk')
               | 
            |
| 74 | 
                +  | 
            |
| 75 | 
                + eqpts = ThermometerEquipmentInfo.objects.filter(point_id=point_id, active_status=ThermometerEquipmentInfo.ONLINE, status=True)  | 
            |
| 76 | 
                + if kw:  | 
            |
| 77 | 
                + eqpts = eqpts.filter(Q(name__icontains=kw) | Q(phone__icontains=kw))  | 
            |
| 78 | 
                + eqpts = [eqpt.data for eqpt in eqpts]  | 
            |
| 79 | 
                +  | 
            |
| 80 | 
                +    return response(data={
               | 
            |
| 81 | 
                + 'eqpts': eqpts,  | 
            |
| 82 | 
                + })  | 
            |
| 83 | 
                +  | 
            |
| 84 | 
                +  | 
            |
| 85 | 
                +@logit  | 
            |
| 86 | 
                +def upload_temperature(request):  | 
            |
| 87 | 
                +    macid = request.POST.get('macid', '')
               | 
            |
| 88 | 
                +    name = request.POST.get('name', '')
               | 
            |
| 89 | 
                +    sex = request.POST.get('sex', 0)
               | 
            |
| 90 | 
                +    age = request.POST.get('age', 0)
               | 
            |
| 91 | 
                +    phone = request.POST.get('phone', '')
               | 
            |
| 92 | 
                +    temperature = request.POST.get('temperature', 0)
               | 
            |
| 93 | 
                +  | 
            |
| 94 | 
                + try:  | 
            |
| 95 | 
                + eqpt = ThermometerEquipmentInfo.objects.get(macid=macid, status=True)  | 
            |
| 96 | 
                + except ThermometerEquipmentInfo.DoesNotExist:  | 
            |
| 97 | 
                + return response(ThermometerEquipmentStatusCode.THERMOMETER_EQUIPMENT_NOT_FOUND)  | 
            |
| 98 | 
                +  | 
            |
| 99 | 
                + try:  | 
            |
| 100 | 
                + point = IsolationPointInfo.objects.get(point_id=eqpt.point_id, status=True)  | 
            |
| 101 | 
                + except IsolationPointInfo.DoesNotExist:  | 
            |
| 102 | 
                + return response(IsolationPointStatusCode.ISOLATIONPOINT_NOT_FOUND)  | 
            |
| 103 | 
                +  | 
            |
| 104 | 
                + point_measure_ymd = tc.local_string(format='%Y-%m-%d')  | 
            |
| 105 | 
                + point_measure_window = point.current_measure_window  | 
            |
| 106 | 
                +  | 
            |
| 107 | 
                + eqpt.name = name  | 
            |
| 108 | 
                + eqpt.sex = sex  | 
            |
| 109 | 
                + eqpt.age = age  | 
            |
| 110 | 
                + eqpt.phone = phone  | 
            |
| 111 | 
                + eqpt.last_submit_at = tc.utc_datetime()  | 
            |
| 112 | 
                + eqpt.save()  | 
            |
| 113 | 
                +  | 
            |
| 114 | 
                + ThermometerMeasureLogInfo.objects.create(point_id=eqpt.point_id, macid=macid, temperature=temperature)  | 
            |
| 115 | 
                +  | 
            |
| 116 | 
                + if point_measure_window:  | 
            |
| 117 | 
                +        ThermometerMeasureInfo.objects.update_or_create(point_id=eqpt.point_id, point_measure_ymd=point_measure_ymd, point_measure_window=point_measure_window, macid=macid, defaults={
               | 
            |
| 118 | 
                + 'temperature': temperature,  | 
            |
| 119 | 
                + })  | 
            |
| 120 | 
                +  | 
            |
| 121 | 
                + return response()  | 
            
                @@ -0,0 +1,25 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from __future__ import division  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +from django_logit import logit  | 
            |
| 6 | 
                +from django_response import response  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +from equipment.models import IsolationPointInfo  | 
            |
| 9 | 
                +from utils.error.errno_utils import IsolationPointStatusCode  | 
            |
| 10 | 
                +  | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                +@logit  | 
            |
| 13 | 
                +def measure_window(request):  | 
            |
| 14 | 
                +    point_id = request.POST.get('point_id', '')
               | 
            |
| 15 | 
                +    point_measure_window = request.POST.get('point_measure_window', '')
               | 
            |
| 16 | 
                +  | 
            |
| 17 | 
                + try:  | 
            |
| 18 | 
                + point = IsolationPointInfo.objects.get(point_id=point_id, status=True)  | 
            |
| 19 | 
                + except IsolationPointInfo.DoesNotExist:  | 
            |
| 20 | 
                + return response(IsolationPointStatusCode.ISOLATIONPOINT_NOT_FOUND)  | 
            |
| 21 | 
                +  | 
            |
| 22 | 
                + point.point_measure_window = point_measure_window  | 
            |
| 23 | 
                + point.save()  | 
            |
| 24 | 
                +  | 
            |
| 25 | 
                + return response()  | 
            
                @@ -2,7 +2,7 @@  | 
            ||
| 2 | 2 | 
                 | 
            
| 3 | 3 | 
                from django.conf.urls import url  | 
            
| 4 | 4 | 
                 | 
            
| 5 | 
                -from api import oauth_views  | 
            |
| 5 | 
                +from api import admin_views, eqpt_views, oauth_views, point_views  | 
            |
| 6 | 6 | 
                 | 
            
| 7 | 7 | 
                 | 
            
| 8 | 8 | 
                urlpatterns = [  | 
            
                @@ -12,3 +12,22 @@ urlpatterns += [  | 
            ||
| 12 | 12 | 
                url(r'^3rd/or$', oauth_views.oauth_redirect, name='3rd_or'),  | 
            
| 13 | 13 | 
                url(r'^3rd/oauth_redirect$', oauth_views.oauth_redirect, name='3rd_oauth_redirect'),  | 
            
| 14 | 14 | 
                ]  | 
            
| 15 | 
                +  | 
            |
| 16 | 
                +urlpatterns += [  | 
            |
| 17 | 
                + url(r'^admin/login$', admin_views.admin_login, name='admin_login'),  | 
            |
| 18 | 
                +]  | 
            |
| 19 | 
                +  | 
            |
| 20 | 
                +urlpatterns += [  | 
            |
| 21 | 
                + url(r'^point/measure_window$', point_views.measure_window, name='measure_window'),  | 
            |
| 22 | 
                +]  | 
            |
| 23 | 
                +  | 
            |
| 24 | 
                +urlpatterns += [  | 
            |
| 25 | 
                + url(r'^eqpt/bind$', eqpt_views.eqpt_bind, name='eqpt_bind'),  | 
            |
| 26 | 
                + url(r'^eqpt/onoff$', eqpt_views.eqpt_onoff, name='eqpt_onoff'),  | 
            |
| 27 | 
                + url(r'^eqpt/list$', eqpt_views.eqpt_list, name='eqpt_list'),  | 
            |
| 28 | 
                + url(r'^eqpt/result$', eqpt_views.eqpt_result, name='eqpt_result'),  | 
            |
| 29 | 
                +]  | 
            |
| 30 | 
                +  | 
            |
| 31 | 
                +urlpatterns += [  | 
            |
| 32 | 
                + url(r'^upload/temperature$', eqpt_views.upload_temperature, name='upload_temperature'),  | 
            |
| 33 | 
                +]  | 
            
                @@ -0,0 +1,31 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.contrib import admin  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +from equipment.models import (IsolationPointInfo, ThermometerEquipmentInfo, ThermometerMeasureInfo,  | 
            |
| 6 | 
                + ThermometerMeasureLogInfo)  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +  | 
            |
| 9 | 
                +class IsolationPointInfoAdmin(admin.ModelAdmin):  | 
            |
| 10 | 
                +    list_display = ('point_id', 'point_name', 'point_measure_window', 'status', 'updated_at', 'created_at')
               | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                +  | 
            |
| 13 | 
                +class ThermometerEquipmentInfoAdmin(admin.ModelAdmin):  | 
            |
| 14 | 
                +    list_display = ('eqpt_id', 'point_id', 'macid', 'sn', 'active_status', 'active_at', 'name', 'sex', 'age', 'phone', 'remark', 'last_submit_at', 'status', 'updated_at', 'created_at')
               | 
            |
| 15 | 
                +    list_filter = ('point_id', 'status')
               | 
            |
| 16 | 
                +  | 
            |
| 17 | 
                +  | 
            |
| 18 | 
                +class ThermometerMeasureInfoAdmin(admin.ModelAdmin):  | 
            |
| 19 | 
                +    list_display = ('point_id', 'point_measure_ymd', 'point_measure_window', 'macid', 'sn', 'temperature', 'status', 'updated_at', 'created_at')
               | 
            |
| 20 | 
                +    list_filter = ('point_id', 'status')
               | 
            |
| 21 | 
                +  | 
            |
| 22 | 
                +  | 
            |
| 23 | 
                +class ThermometerMeasureLogInfoAdmin(admin.ModelAdmin):  | 
            |
| 24 | 
                +    list_display = ('point_id', 'macid', 'sn', 'temperature', 'status', 'updated_at', 'created_at')
               | 
            |
| 25 | 
                +    list_filter = ('point_id', 'status')
               | 
            |
| 26 | 
                +  | 
            |
| 27 | 
                +  | 
            |
| 28 | 
                +admin.site.register(IsolationPointInfo, IsolationPointInfoAdmin)  | 
            |
| 29 | 
                +admin.site.register(ThermometerEquipmentInfo, ThermometerEquipmentInfoAdmin)  | 
            |
| 30 | 
                +admin.site.register(ThermometerMeasureInfo, ThermometerMeasureInfoAdmin)  | 
            |
| 31 | 
                +admin.site.register(ThermometerMeasureLogInfo, ThermometerMeasureLogInfoAdmin)  | 
            
                @@ -0,0 +1,5 @@  | 
            ||
| 1 | 
                +from django.apps import AppConfig  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +class AccountConfig(AppConfig):  | 
            |
| 5 | 
                + name = 'equipment'  | 
            
                @@ -0,0 +1,72 @@  | 
            ||
| 1 | 
                +# Generated by Django 3.2.4 on 2021-07-09 01:41  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.db import migrations, models  | 
            |
| 4 | 
                +import jsonfield.fields  | 
            |
| 5 | 
                +import shortuuidfield.fields  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +class Migration(migrations.Migration):  | 
            |
| 9 | 
                +  | 
            |
| 10 | 
                + initial = True  | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                + dependencies = [  | 
            |
| 13 | 
                + ]  | 
            |
| 14 | 
                +  | 
            |
| 15 | 
                + operations = [  | 
            |
| 16 | 
                + migrations.CreateModel(  | 
            |
| 17 | 
                + name='IsolationPointInfo',  | 
            |
| 18 | 
                + fields=[  | 
            |
| 19 | 
                +                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
               | 
            |
| 20 | 
                +                ('status', models.BooleanField(default=True, help_text='Status', verbose_name='status')),
               | 
            |
| 21 | 
                +                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
               | 
            |
| 22 | 
                +                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
               | 
            |
| 23 | 
                +                ('point_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='隔离点唯一标识', max_length=22, null=True, unique=True)),
               | 
            |
| 24 | 
                +                ('point_name', models.CharField(blank=True, help_text='隔离点名称', max_length=255, null=True, verbose_name='point_name')),
               | 
            |
| 25 | 
                +                ('point_measure_window', jsonfield.fields.JSONField(blank=True, default=[], help_text='隔离点测温时间段', null=True, verbose_name='point_measure_window')),
               | 
            |
| 26 | 
                + ],  | 
            |
| 27 | 
                +            options={
               | 
            |
| 28 | 
                + 'verbose_name': '隔离点信息',  | 
            |
| 29 | 
                + 'verbose_name_plural': '隔离点信息',  | 
            |
| 30 | 
                + },  | 
            |
| 31 | 
                + ),  | 
            |
| 32 | 
                + migrations.CreateModel(  | 
            |
| 33 | 
                + name='ThermometerEquipmentInfo',  | 
            |
| 34 | 
                + fields=[  | 
            |
| 35 | 
                +                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
               | 
            |
| 36 | 
                +                ('status', models.BooleanField(default=True, help_text='Status', verbose_name='status')),
               | 
            |
| 37 | 
                +                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
               | 
            |
| 38 | 
                +                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
               | 
            |
| 39 | 
                +                ('eqpt_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='设备唯一标识', max_length=22, null=True, unique=True)),
               | 
            |
| 40 | 
                +                ('point_id', models.CharField(blank=True, db_index=True, help_text='隔离点唯一标识', max_length=32, null=True, verbose_name='point_id')),
               | 
            |
| 41 | 
                +                ('macid', models.CharField(blank=True, help_text='设备号', max_length=255, null=True, verbose_name='macid')),
               | 
            |
| 42 | 
                +                ('sn', models.CharField(blank=True, help_text='序列号', max_length=255, null=True, verbose_name='sn')),
               | 
            |
| 43 | 
                +                ('active_status', models.IntegerField(choices=[(1, '已激活'), (0, '已离线')], default=0, help_text='激活状态', verbose_name='active_status')),
               | 
            |
| 44 | 
                +                ('active_at', models.DateTimeField(blank=True, help_text='激活时间', null=True, verbose_name='active_at')),
               | 
            |
| 45 | 
                +                ('name', models.CharField(blank=True, help_text='用户姓名', max_length=255, null=True, verbose_name='name')),
               | 
            |
| 46 | 
                +                ('sex', models.IntegerField(choices=[(0, '未知'), (1, '男'), (2, '女')], default=0, help_text='用户性别', verbose_name='sex')),
               | 
            |
| 47 | 
                +                ('age', models.IntegerField(default=0, help_text='用户年龄', verbose_name='age')),
               | 
            |
| 48 | 
                +                ('phone', models.CharField(blank=True, db_index=True, help_text='用户电话', max_length=11, null=True, verbose_name='phone')),
               | 
            |
| 49 | 
                +                ('remark', models.CharField(blank=True, help_text='备注', max_length=255, null=True, verbose_name='remark')),
               | 
            |
| 50 | 
                + ],  | 
            |
| 51 | 
                +            options={
               | 
            |
| 52 | 
                + 'verbose_name': '测温设备信息',  | 
            |
| 53 | 
                + 'verbose_name_plural': '测温设备信息',  | 
            |
| 54 | 
                + },  | 
            |
| 55 | 
                + ),  | 
            |
| 56 | 
                + migrations.CreateModel(  | 
            |
| 57 | 
                + name='ThermometerMeasureInfo',  | 
            |
| 58 | 
                + fields=[  | 
            |
| 59 | 
                +                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
               | 
            |
| 60 | 
                +                ('status', models.BooleanField(default=True, help_text='Status', verbose_name='status')),
               | 
            |
| 61 | 
                +                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
               | 
            |
| 62 | 
                +                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
               | 
            |
| 63 | 
                +                ('macid', models.CharField(blank=True, help_text='设备号', max_length=255, null=True, verbose_name='macid')),
               | 
            |
| 64 | 
                +                ('sn', models.CharField(blank=True, help_text='序列号', max_length=255, null=True, verbose_name='sn')),
               | 
            |
| 65 | 
                +                ('temperature', models.FloatField(default=0, help_text='用户体温', verbose_name='temperature')),
               | 
            |
| 66 | 
                + ],  | 
            |
| 67 | 
                +            options={
               | 
            |
| 68 | 
                + 'verbose_name': '测温记录信息',  | 
            |
| 69 | 
                + 'verbose_name_plural': '测温记录信息',  | 
            |
| 70 | 
                + },  | 
            |
| 71 | 
                + ),  | 
            |
| 72 | 
                + ]  | 
            
                @@ -0,0 +1,18 @@  | 
            ||
| 1 | 
                +# Generated by Django 3.2.4 on 2021-07-09 04:54  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.db import migrations, models  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +class Migration(migrations.Migration):  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                + dependencies = [  | 
            |
| 9 | 
                +        ('equipment', '0001_initial'),
               | 
            |
| 10 | 
                + ]  | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                + operations = [  | 
            |
| 13 | 
                + migrations.AddField(  | 
            |
| 14 | 
                + model_name='thermometermeasureinfo',  | 
            |
| 15 | 
                + name='point_id',  | 
            |
| 16 | 
                + field=models.CharField(blank=True, db_index=True, help_text='隔离点唯一标识', max_length=32, null=True, verbose_name='point_id'),  | 
            |
| 17 | 
                + ),  | 
            |
| 18 | 
                + ]  | 
            
                @@ -0,0 +1,43 @@  | 
            ||
| 1 | 
                +# Generated by Django 3.2.4 on 2021-07-09 05:32  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.db import migrations, models  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +class Migration(migrations.Migration):  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                + dependencies = [  | 
            |
| 9 | 
                +        ('equipment', '0002_thermometermeasureinfo_point_id'),
               | 
            |
| 10 | 
                + ]  | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                + operations = [  | 
            |
| 13 | 
                + migrations.CreateModel(  | 
            |
| 14 | 
                + name='ThermometerMeasureLogInfo',  | 
            |
| 15 | 
                + fields=[  | 
            |
| 16 | 
                +                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
               | 
            |
| 17 | 
                +                ('status', models.BooleanField(default=True, help_text='Status', verbose_name='status')),
               | 
            |
| 18 | 
                +                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
               | 
            |
| 19 | 
                +                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
               | 
            |
| 20 | 
                +                ('point_id', models.CharField(blank=True, db_index=True, help_text='隔离点唯一标识', max_length=32, null=True, verbose_name='point_id')),
               | 
            |
| 21 | 
                +                ('macid', models.CharField(blank=True, help_text='设备号', max_length=255, null=True, verbose_name='macid')),
               | 
            |
| 22 | 
                +                ('sn', models.CharField(blank=True, help_text='序列号', max_length=255, null=True, verbose_name='sn')),
               | 
            |
| 23 | 
                +                ('temperature', models.FloatField(default=0, help_text='用户体温', verbose_name='temperature')),
               | 
            |
| 24 | 
                + ],  | 
            |
| 25 | 
                +            options={
               | 
            |
| 26 | 
                + 'verbose_name': '测温记录信息',  | 
            |
| 27 | 
                + 'verbose_name_plural': '测温记录信息',  | 
            |
| 28 | 
                + },  | 
            |
| 29 | 
                + ),  | 
            |
| 30 | 
                + migrations.AlterModelOptions(  | 
            |
| 31 | 
                + name='thermometermeasureinfo',  | 
            |
| 32 | 
                +            options={'verbose_name': '测温信息', 'verbose_name_plural': '测温信息'},
               | 
            |
| 33 | 
                + ),  | 
            |
| 34 | 
                + migrations.AddField(  | 
            |
| 35 | 
                + model_name='thermometermeasureinfo',  | 
            |
| 36 | 
                + name='point_measure_window',  | 
            |
| 37 | 
                + field=models.CharField(blank=True, db_index=True, help_text='隔离点测温时间段', max_length=16, null=True, verbose_name='point_measure_window'),  | 
            |
| 38 | 
                + ),  | 
            |
| 39 | 
                + migrations.AlterUniqueTogether(  | 
            |
| 40 | 
                + name='thermometermeasureinfo',  | 
            |
| 41 | 
                +            unique_together={('point_id', 'point_measure_window')},
               | 
            |
| 42 | 
                + ),  | 
            |
| 43 | 
                + ]  | 
            
                @@ -0,0 +1,17 @@  | 
            ||
| 1 | 
                +# Generated by Django 3.2.4 on 2021-07-09 05:34  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.db import migrations  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +class Migration(migrations.Migration):  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                + dependencies = [  | 
            |
| 9 | 
                +        ('equipment', '0003_auto_20210709_1332'),
               | 
            |
| 10 | 
                + ]  | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                + operations = [  | 
            |
| 13 | 
                + migrations.AlterUniqueTogether(  | 
            |
| 14 | 
                + name='thermometermeasureinfo',  | 
            |
| 15 | 
                +            unique_together={('point_id', 'point_measure_window', 'macid')},
               | 
            |
| 16 | 
                + ),  | 
            |
| 17 | 
                + ]  | 
            
                @@ -0,0 +1,18 @@  | 
            ||
| 1 | 
                +# Generated by Django 3.2.4 on 2021-07-09 05:42  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.db import migrations, models  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +class Migration(migrations.Migration):  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                + dependencies = [  | 
            |
| 9 | 
                +        ('equipment', '0004_alter_thermometermeasureinfo_unique_together'),
               | 
            |
| 10 | 
                + ]  | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                + operations = [  | 
            |
| 13 | 
                + migrations.AddField(  | 
            |
| 14 | 
                + model_name='thermometerequipmentinfo',  | 
            |
| 15 | 
                + name='last_submit_at',  | 
            |
| 16 | 
                + field=models.DateTimeField(blank=True, help_text='上一次上报时间', null=True, verbose_name='last_submit_at'),  | 
            |
| 17 | 
                + ),  | 
            |
| 18 | 
                + ]  | 
            
                @@ -0,0 +1,22 @@  | 
            ||
| 1 | 
                +# Generated by Django 3.2.4 on 2021-07-09 05:46  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.db import migrations, models  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +class Migration(migrations.Migration):  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                + dependencies = [  | 
            |
| 9 | 
                +        ('equipment', '0005_thermometerequipmentinfo_last_submit_at'),
               | 
            |
| 10 | 
                + ]  | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                + operations = [  | 
            |
| 13 | 
                + migrations.AddField(  | 
            |
| 14 | 
                + model_name='thermometermeasureinfo',  | 
            |
| 15 | 
                + name='point_measure_ymd',  | 
            |
| 16 | 
                + field=models.CharField(blank=True, db_index=True, help_text='隔离点测温日期', max_length=10, null=True, verbose_name='point_measure_ymd'),  | 
            |
| 17 | 
                + ),  | 
            |
| 18 | 
                + migrations.AlterUniqueTogether(  | 
            |
| 19 | 
                + name='thermometermeasureinfo',  | 
            |
| 20 | 
                +            unique_together={('point_id', 'point_measure_ymd', 'point_measure_window', 'macid')},
               | 
            |
| 21 | 
                + ),  | 
            |
| 22 | 
                + ]  | 
            
                @@ -0,0 +1,157 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.db import models  | 
            |
| 4 | 
                +from django.utils.translation import ugettext_lazy as _  | 
            |
| 5 | 
                +from django_models_ext import BaseModelMixin, SexModelMixin  | 
            |
| 6 | 
                +from jsonfield import JSONField  | 
            |
| 7 | 
                +from shortuuidfield import ShortUUIDField  | 
            |
| 8 | 
                +from TimeConvert import TimeConvert as tc  | 
            |
| 9 | 
                +  | 
            |
| 10 | 
                +  | 
            |
| 11 | 
                +class IsolationPointInfo(BaseModelMixin):  | 
            |
| 12 | 
                +    point_id = ShortUUIDField(_('point_id'), max_length=32, blank=True, null=True, help_text='隔离点唯一标识', db_index=True, unique=True)
               | 
            |
| 13 | 
                +    point_name = models.CharField(_('point_name'), max_length=255, blank=True, null=True, help_text='隔离点名称')
               | 
            |
| 14 | 
                +    # [{"start": "8:00", "end": "9:00"}, {"start": "12:00", "end": "14:00"}]
               | 
            |
| 15 | 
                +    point_measure_window = JSONField(_('point_measure_window'), default=[], blank=True, null=True, help_text='隔离点测温时间段')
               | 
            |
| 16 | 
                +  | 
            |
| 17 | 
                + class Meta:  | 
            |
| 18 | 
                +        verbose_name = _('隔离点信息')
               | 
            |
| 19 | 
                +        verbose_name_plural = _('隔离点信息')
               | 
            |
| 20 | 
                +  | 
            |
| 21 | 
                + def __unicode__(self):  | 
            |
| 22 | 
                + return '%d' % self.pk  | 
            |
| 23 | 
                +  | 
            |
| 24 | 
                + @property  | 
            |
| 25 | 
                + def data(self):  | 
            |
| 26 | 
                +        return {
               | 
            |
| 27 | 
                + 'point_id': self.point_id,  | 
            |
| 28 | 
                + 'point_name': self.point_name,  | 
            |
| 29 | 
                + 'point_measure_window': self.point_measure_window,  | 
            |
| 30 | 
                + }  | 
            |
| 31 | 
                +  | 
            |
| 32 | 
                + @property  | 
            |
| 33 | 
                + def current_measure_window(self):  | 
            |
| 34 | 
                + current_ymd = tc.local_string(format='%Y-%m-%d')  | 
            |
| 35 | 
                + current_dt = tc.utc_datetime()  | 
            |
| 36 | 
                + for window in self.point_measure_window:  | 
            |
| 37 | 
                +            start_t, end_t = window.get('start'), window.get('end')
               | 
            |
| 38 | 
                +            start_dt = tc.string_to_utc_datetime(f'{current_ymd} {start_t}:00')
               | 
            |
| 39 | 
                +            end_dt = tc.string_to_utc_datetime(f'{current_ymd} {end_t}:00')
               | 
            |
| 40 | 
                + if start_dt < current_dt < end_dt:  | 
            |
| 41 | 
                +                return f'{start_t}-{end_t}'
               | 
            |
| 42 | 
                + return ''  | 
            |
| 43 | 
                +  | 
            |
| 44 | 
                +  | 
            |
| 45 | 
                +class ThermometerEquipmentInfo(BaseModelMixin):  | 
            |
| 46 | 
                + ONLINE = 1  | 
            |
| 47 | 
                + OFFLINE = 0  | 
            |
| 48 | 
                +  | 
            |
| 49 | 
                + ACTIVE_STATUE_TUPLE = (  | 
            |
| 50 | 
                + (ONLINE, '已激活'),  | 
            |
| 51 | 
                + (OFFLINE, '已离线'),  | 
            |
| 52 | 
                + )  | 
            |
| 53 | 
                +  | 
            |
| 54 | 
                +    eqpt_id = ShortUUIDField(_('eqpt_id'), max_length=32, blank=True, null=True, help_text='设备唯一标识', db_index=True, unique=True)
               | 
            |
| 55 | 
                +  | 
            |
| 56 | 
                +    point_id = models.CharField(_('point_id'), max_length=32, blank=True, null=True, help_text='隔离点唯一标识', db_index=True)
               | 
            |
| 57 | 
                +  | 
            |
| 58 | 
                +    macid = models.CharField(_('macid'), max_length=255, blank=True, null=True, help_text='设备号')
               | 
            |
| 59 | 
                +    sn = models.CharField(_('sn'), max_length=255, blank=True, null=True, help_text='序列号')
               | 
            |
| 60 | 
                +  | 
            |
| 61 | 
                +    active_status = models.IntegerField(_('active_status'), choices=ACTIVE_STATUE_TUPLE, default=OFFLINE, help_text='激活状态')
               | 
            |
| 62 | 
                +    active_at = models.DateTimeField(_('active_at'), blank=True, null=True, help_text=_('激活时间'))
               | 
            |
| 63 | 
                +  | 
            |
| 64 | 
                + # 用户基本信息  | 
            |
| 65 | 
                +    name = models.CharField(_('name'), max_length=255, blank=True, null=True, help_text='用户姓名')
               | 
            |
| 66 | 
                +    sex = models.IntegerField(_('sex'), choices=SexModelMixin.SEX_TUPLE, default=SexModelMixin.UNKNOWN, help_text='用户性别')
               | 
            |
| 67 | 
                +    age = models.IntegerField(_('age'), default=0, help_text='用户年龄')
               | 
            |
| 68 | 
                +    phone = models.CharField(_('phone'), max_length=11, blank=True, null=True, help_text='用户电话', db_index=True)
               | 
            |
| 69 | 
                +  | 
            |
| 70 | 
                +    remark = models.CharField(_('remark'), max_length=255, blank=True, null=True, help_text='备注')
               | 
            |
| 71 | 
                +  | 
            |
| 72 | 
                +    last_submit_at = models.DateTimeField(_('last_submit_at'), blank=True, null=True, help_text=_('上一次上报时间'))
               | 
            |
| 73 | 
                +  | 
            |
| 74 | 
                + class Meta:  | 
            |
| 75 | 
                +        verbose_name = _('测温设备信息')
               | 
            |
| 76 | 
                +        verbose_name_plural = _('测温设备信息')
               | 
            |
| 77 | 
                +  | 
            |
| 78 | 
                + def __unicode__(self):  | 
            |
| 79 | 
                + return '%d' % self.pk  | 
            |
| 80 | 
                +  | 
            |
| 81 | 
                + @property  | 
            |
| 82 | 
                + def data(self):  | 
            |
| 83 | 
                +        return {
               | 
            |
| 84 | 
                + 'eqpt_id': self.eqpt_id,  | 
            |
| 85 | 
                + 'point_id': self.point_id,  | 
            |
| 86 | 
                + 'macid': self.macid,  | 
            |
| 87 | 
                + 'sn': self.sn,  | 
            |
| 88 | 
                + 'active_status': self.active_status,  | 
            |
| 89 | 
                + 'active_status_str': dict(self.ACTIVE_STATUE_TUPLE).get(self.active_status, ''),  | 
            |
| 90 | 
                + 'active_at': tc.local_string(utc_dt=self.active_at),  | 
            |
| 91 | 
                + 'name': self.name or '',  | 
            |
| 92 | 
                + 'sex': self.sex,  | 
            |
| 93 | 
                + 'sex_str': dict(SexModelMixin.SEX_TUPLE).get(self.sex, ''),  | 
            |
| 94 | 
                + 'age': self.age or '',  | 
            |
| 95 | 
                + 'phone': self.phone or '',  | 
            |
| 96 | 
                + 'remark': self.remark or '',  | 
            |
| 97 | 
                + 'last_submit_at': tc.local_string(utc_dt=self.last_submit_at),  | 
            |
| 98 | 
                + 'created_at': tc.local_string(utc_dt=self.created_at),  | 
            |
| 99 | 
                + }  | 
            |
| 100 | 
                +  | 
            |
| 101 | 
                +  | 
            |
| 102 | 
                +class ThermometerMeasureInfo(BaseModelMixin):  | 
            |
| 103 | 
                +    point_id = models.CharField(_('point_id'), max_length=32, blank=True, null=True, help_text='隔离点唯一标识', db_index=True)
               | 
            |
| 104 | 
                +  | 
            |
| 105 | 
                +    point_measure_ymd = models.CharField(_('point_measure_ymd'), max_length=10, blank=True, null=True, help_text='隔离点测温日期', db_index=True)
               | 
            |
| 106 | 
                +    point_measure_window = models.CharField(_('point_measure_window'), max_length=16, blank=True, null=True, help_text='隔离点测温时间段', db_index=True)
               | 
            |
| 107 | 
                +  | 
            |
| 108 | 
                +    macid = models.CharField(_('macid'), max_length=255, blank=True, null=True, help_text='设备号')
               | 
            |
| 109 | 
                +    sn = models.CharField(_('sn'), max_length=255, blank=True, null=True, help_text='序列号')
               | 
            |
| 110 | 
                +  | 
            |
| 111 | 
                +    temperature = models.FloatField(_('temperature'), default=0, help_text='用户体温')
               | 
            |
| 112 | 
                +  | 
            |
| 113 | 
                + class Meta:  | 
            |
| 114 | 
                +        verbose_name = _('测温信息')
               | 
            |
| 115 | 
                +        verbose_name_plural = _('测温信息')
               | 
            |
| 116 | 
                +  | 
            |
| 117 | 
                + unique_together = (  | 
            |
| 118 | 
                +            ('point_id', 'point_measure_ymd', 'point_measure_window', 'macid')
               | 
            |
| 119 | 
                + )  | 
            |
| 120 | 
                +  | 
            |
| 121 | 
                + def __unicode__(self):  | 
            |
| 122 | 
                + return '%d' % self.pk  | 
            |
| 123 | 
                +  | 
            |
| 124 | 
                + @property  | 
            |
| 125 | 
                + def data(self):  | 
            |
| 126 | 
                +        return {
               | 
            |
| 127 | 
                + 'point_id': self.point_id,  | 
            |
| 128 | 
                + 'point_measure_window': self.point_measure_window,  | 
            |
| 129 | 
                + 'macid': self.macid,  | 
            |
| 130 | 
                + 'sn': self.sn,  | 
            |
| 131 | 
                + 'temperature': self.temperature,  | 
            |
| 132 | 
                + }  | 
            |
| 133 | 
                +  | 
            |
| 134 | 
                +  | 
            |
| 135 | 
                +class ThermometerMeasureLogInfo(BaseModelMixin):  | 
            |
| 136 | 
                +    point_id = models.CharField(_('point_id'), max_length=32, blank=True, null=True, help_text='隔离点唯一标识', db_index=True)
               | 
            |
| 137 | 
                +  | 
            |
| 138 | 
                +    macid = models.CharField(_('macid'), max_length=255, blank=True, null=True, help_text='设备号')
               | 
            |
| 139 | 
                +    sn = models.CharField(_('sn'), max_length=255, blank=True, null=True, help_text='序列号')
               | 
            |
| 140 | 
                +  | 
            |
| 141 | 
                +    temperature = models.FloatField(_('temperature'), default=0, help_text='用户体温')
               | 
            |
| 142 | 
                +  | 
            |
| 143 | 
                + class Meta:  | 
            |
| 144 | 
                +        verbose_name = _('测温记录信息')
               | 
            |
| 145 | 
                +        verbose_name_plural = _('测温记录信息')
               | 
            |
| 146 | 
                +  | 
            |
| 147 | 
                + def __unicode__(self):  | 
            |
| 148 | 
                + return '%d' % self.pk  | 
            |
| 149 | 
                +  | 
            |
| 150 | 
                + @property  | 
            |
| 151 | 
                + def data(self):  | 
            |
| 152 | 
                +        return {
               | 
            |
| 153 | 
                + 'point_id': self.point_id,  | 
            |
| 154 | 
                + 'macid': self.macid,  | 
            |
| 155 | 
                + 'sn': self.sn,  | 
            |
| 156 | 
                + 'temperature': self.temperature,  | 
            |
| 157 | 
                + }  | 
            
                @@ -0,0 +1,4 @@  | 
            ||
| 1 | 
                +from django.test import TestCase  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +# Create your tests here.  | 
            
                @@ -0,0 +1,4 @@  | 
            ||
| 1 | 
                +from django.shortcuts import render  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +# Create your views here.  | 
            
                @@ -5,21 +5,23 @@ from django.utils.translation import ugettext_lazy as _  | 
            ||
| 5 | 5 | 
                 | 
            
| 6 | 6 | 
                 | 
            
| 7 | 7 | 
                class BaseModelMixin(models.Model):  | 
            
| 8 | 
                - status = models.BooleanField(_(u'status'), default=True, help_text=_(u'状态'))  | 
            |
| 9 | 
                - created_at = models.DateTimeField(_(u'created_at'), auto_now_add=True, editable=True, help_text=_(u'创建时间'))  | 
            |
| 10 | 
                - updated_at = models.DateTimeField(_(u'updated_at'), auto_now=True, editable=True, help_text=_(u'更新时间'))  | 
            |
| 8 | 
                +    status = models.BooleanField(_('status'), default=True, help_text=_('状态'), db_index=True)
               | 
            |
| 9 | 
                +    created_at = models.DateTimeField(_('created_at'), auto_now_add=True, editable=True, help_text=_('创建时间'))
               | 
            |
| 10 | 
                +    updated_at = models.DateTimeField(_('updated_at'), auto_now=True, editable=True, help_text=_('更新时间'))
               | 
            |
| 11 | 11 | 
                 | 
            
| 12 | 12 | 
                class Meta:  | 
            
| 13 | 13 | 
                abstract = True  | 
            
| 14 | 14 | 
                 | 
            
| 15 | 15 | 
                 | 
            
| 16 | 16 | 
                class SexChoicesMixin(models.Model):  | 
            
| 17 | 
                + UNKNOWN = 0  | 
            |
| 17 | 18 | 
                MALE = 1  | 
            
| 18 | 
                - FEMALE = 0  | 
            |
| 19 | 
                + FEMALE = 2  | 
            |
| 19 | 20 | 
                 | 
            
| 20 | 
                - SEX_TYPE = (  | 
            |
| 21 | 
                - (MALE, u'男'),  | 
            |
| 22 | 
                - (FEMALE, u'女'),  | 
            |
| 21 | 
                + SEX_TUPLE = (  | 
            |
| 22 | 
                + (UNKNOWN, '未知'),  | 
            |
| 23 | 
                + (MALE, '男'),  | 
            |
| 24 | 
                + (FEMALE, '女'),  | 
            |
| 23 | 25 | 
                )  | 
            
| 24 | 26 | 
                 | 
            
| 25 | 27 | 
                class Meta:  | 
            
                @@ -14,4 +14,4 @@ EMAIL_HOST_USER = 'error.notify@exmail.com'  | 
            ||
| 14 | 14 | 
                EMAIL_HOST_PASSWORD = '<^_^>pwd<^_^>'  | 
            
| 15 | 15 | 
                DEFAULT_FROM_EMAIL = 'error.notify <error.notify@exmail.com>'  | 
            
| 16 | 16 | 
                 ADMINS = [('Zhang San', 'san.zhang@exmail.com'), ('Li Si', 'si.li@exmail.com')]
               | 
            
| 17 | 
                -EMAIL_SUBJECT_PREFIX = u'[Thermometer] '  | 
            |
| 17 | 
                +EMAIL_SUBJECT_PREFIX = '[Thermometer] '  | 
            
                @@ -55,6 +55,8 @@ INSTALLED_APPS = [  | 
            ||
| 55 | 55 | 
                'django_we',  | 
            
| 56 | 56 | 
                'commands',  | 
            
| 57 | 57 | 
                'api',  | 
            
| 58 | 
                + 'account',  | 
            |
| 59 | 
                + 'equipment',  | 
            |
| 58 | 60 | 
                ]  | 
            
| 59 | 61 | 
                 | 
            
| 60 | 62 | 
                MIDDLEWARE = [  | 
            
                @@ -278,7 +280,7 @@ ADMINS = []  | 
            ||
| 278 | 280 | 
                MANAGERS = ADMINS  | 
            
| 279 | 281 | 
                # Subject-line prefix for email messages send with django.core.mail.mail_admins  | 
            
| 280 | 282 | 
                # or ...mail_managers. Make sure to include the trailing space.  | 
            
| 281 | 
                -EMAIL_SUBJECT_PREFIX = u'[Thermometer] '  | 
            |
| 283 | 
                +EMAIL_SUBJECT_PREFIX = '[Thermometer] '  | 
            |
| 282 | 284 | 
                 | 
            
| 283 | 285 | 
                # Django-Admin Settings  | 
            
| 284 | 286 | 
                DJANGO_ADMIN_DISABLE_DELETE_SELECTED = False  | 
            
                @@ -310,6 +312,10 @@ DJANGO_WE_MODEL_DISPLAY_OR_NOT = True  | 
            ||
| 310 | 312 | 
                DJANGO_WE_COOKIE_MAX_AGE = COOKIE_MAX_AGE  | 
            
| 311 | 313 | 
                DJANGO_WE_COOKIE_SALT = COOKIE_SALT  | 
            
| 312 | 314 | 
                 | 
            
| 315 | 
                +# 密码设置  | 
            |
| 316 | 
                +MAKE_PASSWORD_SALT = ''  | 
            |
| 317 | 
                +MAKE_PASSWORD_HASHER = 'pbkdf2_sha256'  | 
            |
| 318 | 
                +  | 
            |
| 313 | 319 | 
                # 开发调试相关配置  | 
            
| 314 | 320 | 
                if DEBUG:  | 
            
| 315 | 321 | 
                try:  | 
            
                @@ -5,32 +5,55 @@ from StatusCode import BaseStatusCode, StatusCodeField  | 
            ||
| 5 | 5 | 
                 | 
            
| 6 | 6 | 
                class ParamStatusCode(BaseStatusCode):  | 
            
| 7 | 7 | 
                """ 4000xx 参数相关错误码 """  | 
            
| 8 | 
                - PARAM_NOT_FOUND = StatusCodeField(400000, 'Param Not Found', description=u'参数不存在')  | 
            |
| 8 | 
                + PARAM_NOT_FOUND = StatusCodeField(400000, 'Param Not Found', description='参数不存在')  | 
            |
| 9 | 9 | 
                 | 
            
| 10 | 10 | 
                 | 
            
| 11 | 11 | 
                class ProfileStatusCode(BaseStatusCode):  | 
            
| 12 | 12 | 
                """ 4001xx 用户相关错误码 """  | 
            
| 13 | 
                - PROFILE_NOT_FOUND = StatusCodeField(400101, 'Profile Not Found', description=u'用户不存在')  | 
            |
| 13 | 
                + PROFILE_NOT_FOUND = StatusCodeField(400101, 'Profile Not Found', description='用户不存在')  | 
            |
| 14 | 14 | 
                 | 
            
| 15 | 15 | 
                 | 
            
| 16 | 16 | 
                class PhoneStatusCode(BaseStatusCode):  | 
            
| 17 | 17 | 
                """ 4002xx 手机相关错误码 """  | 
            
| 18 | 
                - INVALID_PHONE = StatusCodeField(400200, 'Invalid Phone', description=u'非法手机号')  | 
            |
| 19 | 
                - PHONE_NOT_FOUND = StatusCodeField(400201, 'Phone Not Found', description=u'手机号不存在')  | 
            |
| 20 | 
                - PHONE_ALREADY_EXISTS = StatusCodeField(400202, 'Phone Already Exists', description=u'手机号已存在')  | 
            |
| 18 | 
                + INVALID_PHONE = StatusCodeField(400200, 'Invalid Phone', description='非法手机号')  | 
            |
| 19 | 
                + PHONE_NOT_FOUND = StatusCodeField(400201, 'Phone Not Found', description='手机号不存在')  | 
            |
| 20 | 
                + PHONE_ALREADY_EXISTS = StatusCodeField(400202, 'Phone Already Exists', description='手机号已存在')  | 
            |
| 21 | 
                +  | 
            |
| 22 | 
                +  | 
            |
| 23 | 
                +class AdministratorStatusCode(BaseStatusCode):  | 
            |
| 24 | 
                + """ 操作员相关错误码 4010xx """  | 
            |
| 25 | 
                + ADMINISTRATOR_NOT_FOUND = StatusCodeField(401001, 'Administrator Not Found', description='管理员不存在')  | 
            |
| 26 | 
                + # 密码  | 
            |
| 27 | 
                + ADMINISTRATOR_PASSWORD_ERROR = StatusCodeField(401002, 'Administrator Password Error', description='管理员密码错误')  | 
            |
| 28 | 
                + # 手机号  | 
            |
| 29 | 
                + ADMINISTRATOR_PHONE_ALREADY_EXISTS = StatusCodeField(401005, 'Administrator Phone Already Exists', description='管理员手机号已经存在')  | 
            |
| 30 | 
                + # 状态  | 
            |
| 31 | 
                + ADMINISTRATOR_NOT_ACTIVATED = StatusCodeField(401015, 'Administrator Not Activated', description='管理员未激活')  | 
            |
| 32 | 
                + ADMINISTRATOR_HAS_DISABLED = StatusCodeField(401016, 'Administrator Has Disabled', description='管理员已禁用')  | 
            |
| 33 | 
                + ADMINISTRATOR_HAS_DELETED = StatusCodeField(401017, 'Administrator Has Deleted', description='管理员已删除')  | 
            |
| 34 | 
                +  | 
            |
| 35 | 
                +  | 
            |
| 36 | 
                +class IsolationPointStatusCode(BaseStatusCode):  | 
            |
| 37 | 
                + """ 操作员相关错误码 4020xx """  | 
            |
| 38 | 
                + ISOLATIONPOINT_NOT_FOUND = StatusCodeField(402001, 'IsolationPoint Not Found', description='隔离点不存在')  | 
            |
| 39 | 
                +  | 
            |
| 40 | 
                +  | 
            |
| 41 | 
                +class ThermometerEquipmentStatusCode(BaseStatusCode):  | 
            |
| 42 | 
                + """ 操作员相关错误码 4030xx """  | 
            |
| 43 | 
                + THERMOMETER_EQUIPMENT_NOT_FOUND = StatusCodeField(403001, 'Thermometer Equipment Not Found', description='测温设备不存在')  | 
            |
| 21 | 44 | 
                 | 
            
| 22 | 45 | 
                 | 
            
| 23 | 46 | 
                class OrderStatusCode(BaseStatusCode):  | 
            
| 24 | 47 | 
                """ 4040xx 订单/支付相关错误码 """  | 
            
| 25 | 
                - UNIFIED_ORDER_FAIL = StatusCodeField(404000, 'Unified Order Fail', description=u'统一下单失败')  | 
            |
| 26 | 
                - ORDER_NOT_FOUND = StatusCodeField(404001, 'Order Not Found', description=u'订单不存在')  | 
            |
| 48 | 
                + UNIFIED_ORDER_FAIL = StatusCodeField(404000, 'Unified Order Fail', description='统一下单失败')  | 
            |
| 49 | 
                + ORDER_NOT_FOUND = StatusCodeField(404001, 'Order Not Found', description='订单不存在')  | 
            |
| 27 | 50 | 
                # 订单支付状态  | 
            
| 28 | 
                - ORDER_NOT_PAY = StatusCodeField(404011, 'Order Not Pay', description=u'订单未支付')  | 
            |
| 29 | 
                - ORDER_PAYING = StatusCodeField(404012, 'Order Paying', description=u'订单支付中')  | 
            |
| 30 | 
                - ORDER_PAY_FAIL = StatusCodeField(404013, 'Order Pay Fail', description=u'微信支付失败')  | 
            |
| 51 | 
                + ORDER_NOT_PAY = StatusCodeField(404011, 'Order Not Pay', description='订单未支付')  | 
            |
| 52 | 
                + ORDER_PAYING = StatusCodeField(404012, 'Order Paying', description='订单支付中')  | 
            |
| 53 | 
                + ORDER_PAY_FAIL = StatusCodeField(404013, 'Order Pay Fail', description='微信支付失败')  | 
            |
| 31 | 54 | 
                # 通知校验状态  | 
            
| 32 | 
                - SIGN_CHECK_FAIL = StatusCodeField(404090, 'Sign Check Fail', description=u'签名校验失败')  | 
            |
| 33 | 
                - FEE_CHECK_FAIL = StatusCodeField(404091, 'FEE Check Fail', description=u'金额校验失败')  | 
            |
| 55 | 
                + SIGN_CHECK_FAIL = StatusCodeField(404090, 'Sign Check Fail', description='签名校验失败')  | 
            |
| 56 | 
                + FEE_CHECK_FAIL = StatusCodeField(404091, 'FEE Check Fail', description='金额校验失败')  | 
            |
| 34 | 57 | 
                 | 
            
| 35 | 58 | 
                 | 
            
| 36 | 59 | 
                class PayStatusCode(BaseStatusCode):  | 
            
                @@ -39,39 +62,39 @@ class PayStatusCode(BaseStatusCode):  | 
            ||
| 39 | 62 | 
                 | 
            
| 40 | 63 | 
                class WithdrawStatusCode(BaseStatusCode):  | 
            
| 41 | 64 | 
                """ 4042xx 提现相关错误码 """  | 
            
| 42 | 
                - BALANCE_INSUFFICIENT = StatusCodeField(404200, 'Balance Insufficient', description=u'提现金额不足')  | 
            |
| 65 | 
                + BALANCE_INSUFFICIENT = StatusCodeField(404200, 'Balance Insufficient', description='提现金额不足')  | 
            |
| 43 | 66 | 
                 | 
            
| 44 | 67 | 
                 | 
            
| 45 | 68 | 
                class TokenStatusCode(BaseStatusCode):  | 
            
| 46 | 69 | 
                """ 4090xx 票据相关错误码 """  | 
            
| 47 | 
                - TOKEN_NOT_FOUND = StatusCodeField(409001, 'Token Not Found', description=u'票据不存在')  | 
            |
| 70 | 
                + TOKEN_NOT_FOUND = StatusCodeField(409001, 'Token Not Found', description='票据不存在')  | 
            |
| 48 | 71 | 
                 | 
            
| 49 | 72 | 
                 | 
            
| 50 | 73 | 
                class SignatureStatusCode(BaseStatusCode):  | 
            
| 51 | 74 | 
                """ 4091xx 签名校验错误 """  | 
            
| 52 | 
                - SIGNATURE_ERROR = StatusCodeField(409101, 'Signature Error', description=u'签名错误')  | 
            |
| 75 | 
                + SIGNATURE_ERROR = StatusCodeField(409101, 'Signature Error', description='签名错误')  | 
            |
| 53 | 76 | 
                 | 
            
| 54 | 77 | 
                 | 
            
| 55 | 78 | 
                class GVCodeStatusCode(BaseStatusCode):  | 
            
| 56 | 79 | 
                """ 4092xx 图形验证码相关错误码 """  | 
            
| 57 | 
                - GRAPHIC_VCODE_ERROR = StatusCodeField(409201, 'Graphic VCode Error', description=u'图形验证码错误')  | 
            |
| 80 | 
                + GRAPHIC_VCODE_ERROR = StatusCodeField(409201, 'Graphic VCode Error', description='图形验证码错误')  | 
            |
| 58 | 81 | 
                 | 
            
| 59 | 82 | 
                 | 
            
| 60 | 83 | 
                class SVCodeStatusCode(BaseStatusCode):  | 
            
| 61 | 84 | 
                """ 4093xx 短信验证码相关错误码 """  | 
            
| 62 | 
                - SMS_QUOTA_LIMIT = StatusCodeField(409300, 'SMS Quota Limit', description=u'短信次数超限')  | 
            |
| 63 | 
                - SMS_VCODE_ERROR = StatusCodeField(409301, 'SMS VCode Error', description=u'验证码错误,请稍后重试')  | 
            |
| 64 | 
                - SMS_VCODE_HAS_SEND = StatusCodeField(409302, 'SMS VCode Has Send', description=u'验证码已发送,请勿重复获取')  | 
            |
| 85 | 
                + SMS_QUOTA_LIMIT = StatusCodeField(409300, 'SMS Quota Limit', description='短信次数超限')  | 
            |
| 86 | 
                + SMS_VCODE_ERROR = StatusCodeField(409301, 'SMS VCode Error', description='验证码错误,请稍后重试')  | 
            |
| 87 | 
                + SMS_VCODE_HAS_SEND = StatusCodeField(409302, 'SMS VCode Has Send', description='验证码已发送,请勿重复获取')  | 
            |
| 65 | 88 | 
                 | 
            
| 66 | 89 | 
                 | 
            
| 67 | 90 | 
                class InsufficientStatusCode(BaseStatusCode):  | 
            
| 68 | 91 | 
                """ 4095xx 不足相关错误码 """  | 
            
| 69 | 
                - BALANCE_INSUFFICIENT = StatusCodeField(409501, 'Balance Insufficient', description=u'余额不足')  | 
            |
| 70 | 
                - INTEGRAL_INSUFFICIENT = StatusCodeField(409502, 'Integral Insufficient', description=u'积分不足')  | 
            |
| 92 | 
                + BALANCE_INSUFFICIENT = StatusCodeField(409501, 'Balance Insufficient', description='余额不足')  | 
            |
| 93 | 
                + INTEGRAL_INSUFFICIENT = StatusCodeField(409502, 'Integral Insufficient', description='积分不足')  | 
            |
| 71 | 94 | 
                 | 
            
| 72 | 95 | 
                 | 
            
| 73 | 96 | 
                class PermissionStatusCode(BaseStatusCode):  | 
            
| 74 | 97 | 
                """ 4099xx 权限相关错误码 """  | 
            
| 75 | 
                - PERMISSION_DENIED = StatusCodeField(409900, 'Permission Denied', description=u'权限不足')  | 
            |
| 76 | 
                - UPLOAD_PERMISSION_DENIED = StatusCodeField(409910, 'Upload Permission Denied', description=u'上传权限不足')  | 
            |
| 77 | 
                - UPDATE_PERMISSION_DENIED = StatusCodeField(409930, 'Update Permission Denied', description=u'更新权限不足')  | 
            |
| 98 | 
                + PERMISSION_DENIED = StatusCodeField(409900, 'Permission Denied', description='权限不足')  | 
            |
| 99 | 
                + UPLOAD_PERMISSION_DENIED = StatusCodeField(409910, 'Upload Permission Denied', description='上传权限不足')  | 
            |
| 100 | 
                + UPDATE_PERMISSION_DENIED = StatusCodeField(409930, 'Update Permission Denied', description='更新权限不足')  |